package com.nuanshui.heatedloan.task.spring;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Binding.DestinationType;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListenerConfigurer;
import org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.listener.MethodRabbitListenerEndpoint;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.BeanExpressionResolver;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.expression.StandardBeanExpressionResolver;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory;
import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
import org.springframework.util.Assert;
import org.springframework.util.MethodInvoker;
import org.springframework.util.ReflectionUtils;

import com.google.common.collect.Maps;
import com.nuanshui.heatedloan.task.annotation.Task;
import com.nuanshui.heatedloan.task.constant.LogAdapterType;
import com.nuanshui.heatedloan.task.constant.TaskGlobalParams;
import com.nuanshui.heatedloan.task.logging.LogAdapter;
import com.nuanshui.heatedloan.task.logging.LogAdapterFactory;
import com.nuanshui.heatedloan.task.proxy.MessageListenerBeanClassFactory;
import com.nuanshui.heatedloan.task.proxy.MessageListenerBeanFactoryBuilder;
import com.nuanshui.heatedloan.task.proxy.MetaClass;
import com.nuanshui.heatedloan.task.util.CommonUtil;
import com.nuanshui.heatedloan.task.util.ExcutorTaskMsg;

/**
 * 定时任务注解处理器
 * @param 
 * @author guohao
 * @date 2017年2月13日 上午10:02:56
 * @since $
 */
public class TaskProcessor  implements  BeanPostProcessor, Ordered, BeanFactoryAware, SmartInitializingSingleton,InitializingBean{

	
	static final String DEFAULT_RABBIT_LISTENER_CONTAINER_FACTORY_BEAN_NAME = "rabbitListenerContainerFactory";


	private RabbitListenerEndpointRegistry endpointRegistry;

	private String containerFactoryBeanName = DEFAULT_RABBIT_LISTENER_CONTAINER_FACTORY_BEAN_NAME;

	private BeanFactory beanFactory;

	private final RabbitHandlerMethodFactoryAdapter messageHandlerMethodFactory =
			new RabbitHandlerMethodFactoryAdapter();

	private final RabbitListenerEndpointRegistrar registrar = new RabbitListenerEndpointRegistrar();

	private final AtomicInteger counter = new AtomicInteger();

	private BeanExpressionResolver resolver = new StandardBeanExpressionResolver();

	private BeanExpressionContext expressionContext;

	@Override
	public int getOrder() {
		return LOWEST_PRECEDENCE;
	}

	/**
	 * Set the {@link RabbitListenerEndpointRegistry} that will hold the created
	 * endpoint and manage the lifecycle of the related listener container.
	 * @param endpointRegistry the {@link RabbitListenerEndpointRegistry} to set.
	 */
	public void setEndpointRegistry(RabbitListenerEndpointRegistry endpointRegistry) {
		this.endpointRegistry = endpointRegistry;
	}

	/**
	 * Set the name of the {@link RabbitListenerContainerFactory} to use by default.
	 * <p>If none is specified, "rabbitListenerContainerFactory" is assumed to be defined.
	 * @param containerFactoryBeanName the {@link RabbitListenerContainerFactory} bean name.
	 */
	public void setContainerFactoryBeanName(String containerFactoryBeanName) {
		this.containerFactoryBeanName = containerFactoryBeanName;
	}

	/**
	 * Set the {@link MessageHandlerMethodFactory} to use to configure the message
	 * listener responsible to serve an endpoint detected by this processor.
	 * <p>By default, {@link DefaultMessageHandlerMethodFactory} is used and it
	 * can be configured further to support additional method arguments
	 * or to customize conversion and validation support. See
	 * {@link DefaultMessageHandlerMethodFactory} Javadoc for more details.
	 * @param messageHandlerMethodFactory the {@link MessageHandlerMethodFactory} instance.
	 */
	public void setMessageHandlerMethodFactory(MessageHandlerMethodFactory messageHandlerMethodFactory) {
		this.messageHandlerMethodFactory.setMessageHandlerMethodFactory(messageHandlerMethodFactory);
	}

	/**
	 * Making a {@link BeanFactory} available is optional; if not set,
	 * {@link RabbitListenerConfigurer} beans won't get autodetected and an
	 * {@link #setEndpointRegistry endpoint registry} has to be explicitly configured.
	 * @param beanFactory the {@link BeanFactory} to be used.
	 */
	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		this.beanFactory = beanFactory;
		if (beanFactory instanceof ConfigurableListableBeanFactory) {
			this.resolver = ((ConfigurableListableBeanFactory) beanFactory).getBeanExpressionResolver();
			this.expressionContext = new BeanExpressionContext((ConfigurableListableBeanFactory) beanFactory, null);
		}
	}


	@Override
	public void afterSingletonsInstantiated() {
		this.registrar.setBeanFactory(this.beanFactory);

		if (this.beanFactory instanceof ListableBeanFactory) {
			Map<String, RabbitListenerConfigurer> instances =
					((ListableBeanFactory) this.beanFactory).getBeansOfType(RabbitListenerConfigurer.class);
			for (RabbitListenerConfigurer configurer : instances.values()) {
				configurer.configureRabbitListeners(this.registrar);
			}
		}

		if (this.registrar.getEndpointRegistry() == null) {
			if (this.endpointRegistry == null) {
				Assert.state(this.beanFactory != null,
						"BeanFactory must be set to find endpoint registry by bean name");
				this.endpointRegistry = this.beanFactory.getBean(
						RabbitListenerConfigUtils.RABBIT_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME,
						RabbitListenerEndpointRegistry.class);
			}
			this.registrar.setEndpointRegistry(this.endpointRegistry);
		}

		if (this.containerFactoryBeanName != null) {
			this.registrar.setContainerFactoryBeanName(this.containerFactoryBeanName);
		}

		// Set the custom handler method factory once resolved by the configurer
		MessageHandlerMethodFactory handlerMethodFactory = this.registrar.getMessageHandlerMethodFactory();
		if (handlerMethodFactory != null) {
			this.messageHandlerMethodFactory.setMessageHandlerMethodFactory(handlerMethodFactory);
		}

		// Actually register all listeners
		this.registrar.afterPropertiesSet();
	}


	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
		Class<?> targetClass = AopUtils.getTargetClass(bean);
		ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
			@Override
			public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
				Task task = AnnotationUtils.getAnnotation(method, Task.class);
				if (task != null) {
					try {
						processTaskListener(task, method, bean);
					} catch (Exception e) {
						e.printStackTrace();
					}
					
				}
			}
		});
		return bean;
	}

	protected void processTaskListener(Task task, Method method, Object bean) throws Exception {
		if (AopUtils.isJdkDynamicProxy(bean)) {
			try {
				method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
			}
			catch (SecurityException ex) {
				ReflectionUtils.handleReflectionException(ex);
			}
			catch (NoSuchMethodException ex) {
			    ex.printStackTrace();
			}
		}
		
		//生成 methodInvoker  
		MethodInvoker methodInvoker  = new MethodInvoker();
		methodInvoker.setTargetClass(bean.getClass());
		methodInvoker.setTargetMethod(method.getName());
		methodInvoker.setTargetObject(bean); 
		
		
		String value = CommonUtil.generateMessageQueueName(task.group(),task.name()); 

		//动态生成接受消息的对象
		String beanName = new StringBuffer(TaskGlobalParams.JOB_BEAN_PREFIX).append(task.group()).append(task.name()).toString();
		MessageListenerBeanClassFactory classFactory = MessageListenerBeanFactoryBuilder.build();
		MetaClass metaClass = classFactory.createProxyClass(beanName);
	    Object listenerBean = metaClass.getInstance();
	    //设置invoker
	    Method setMethodInvokerMethod =  metaClass.getSetMethod("methodInvoker");
	    setMethodInvokerMethod.invoke(listenerBean,methodInvoker);
	    
	    //设置 executorTaskMsg
	    ExcutorTaskMsg excutorTaskMsg =  beanFactory.getBean(ExcutorTaskMsg.class);
	    Method setExecutorTaskMsgMethod =  metaClass.getSetMethod("excutorTaskMsg");
	    setExecutorTaskMsgMethod.invoke(listenerBean,excutorTaskMsg);
	    
	    //设置 task
	    
	    Method setTaskMethod =  metaClass.getSetMethod("task");
	    setTaskMethod.invoke(listenerBean, task);
	    
	    
		//生成队列
	    RabbitAdmin rabbitAdmin =  this.beanFactory.getBean(RabbitAdmin.class);
	    //声明交换机exchange
	    String exchangeName = TaskGlobalParams.JOB_MESSAGE_EXCHANGE;
	    Exchange  exchange = new DirectExchange(exchangeName,true,false);
	    rabbitAdmin.declareExchange(exchange);
	    //声明队列
		Queue queue = new Queue(value,true,false,false);
		rabbitAdmin.declareQueue(queue);
		HashMap<String, Object> args = Maps.newHashMap();
		Binding binding = new Binding(value, DestinationType.QUEUE, exchangeName,value,args);
		rabbitAdmin.declareBinding(binding);
	    
	    
		//绑定队列
		MethodRabbitListenerEndpoint endpoint = new MethodRabbitListenerEndpoint();
		endpoint.setBean(listenerBean);
		endpoint.setMethod(metaClass.getTargetMethod());
		endpoint.setMessageHandlerMethodFactory(this.messageHandlerMethodFactory);
		endpoint.setId(value);
		endpoint.setQueueNames(value);
        RabbitListenerContainerFactory<?> factory = this.beanFactory.getBean(RabbitListenerContainerFactory.class);
		this.registrar.registerEndpoint(endpoint, factory);
	}





	/**
	 * An {@link MessageHandlerMethodFactory} adapter that offers a configurable underlying
	 * instance to use. Useful if the factory to use is determined once the endpoints
	 * have been registered but not created yet.
	 * @see RabbitListenerEndpointRegistrar#setMessageHandlerMethodFactory
	 */
	private class RabbitHandlerMethodFactoryAdapter implements MessageHandlerMethodFactory {

		private MessageHandlerMethodFactory messageHandlerMethodFactory;

		public void setMessageHandlerMethodFactory(MessageHandlerMethodFactory rabbitHandlerMethodFactory1) {
			this.messageHandlerMethodFactory = rabbitHandlerMethodFactory1;
		}

		@Override
		public InvocableHandlerMethod createInvocableHandlerMethod(Object bean, Method method) {
			return getMessageHandlerMethodFactory().createInvocableHandlerMethod(bean, method);
		}

		private MessageHandlerMethodFactory getMessageHandlerMethodFactory() {
			if (this.messageHandlerMethodFactory == null) {
				this.messageHandlerMethodFactory = createDefaultMessageHandlerMethodFactory();
			}
			return this.messageHandlerMethodFactory;
		}

		private MessageHandlerMethodFactory createDefaultMessageHandlerMethodFactory() {
			DefaultMessageHandlerMethodFactory defaultFactory = new DefaultMessageHandlerMethodFactory();
			defaultFactory.setBeanFactory(beanFactory);
			defaultFactory.afterPropertiesSet();
			return defaultFactory;
		}

	}





	@Override
	public void afterPropertiesSet() throws Exception {
		try {
			LogAdapter logAdapter =  LogAdapterFactory.createLogAdapter(LogAdapterType.LOGBACK);
			if(logAdapter !=null) {
				logAdapter.start(this.beanFactory);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
}
